package org.apitool.main;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;

import com.alibaba.fastjson.JSONObject;

public class HttpClient {
	public static String GET_ACCESS_TOKEN_URL = "https://member.meizu.com/oauth/access_token";

	public static final int LOGIN_TYPE_CLIENT = 1;
	public static final int LOGIN_TYPE_SN = 2;

	public static final String METHOD_POST = "POST";
	public static final String METHOD_GET = "GET";

	private final Random m_tRandom = new Random();
	private final Base64Encoder m_tBase64Encoder = new Base64Encoder();

	private String m_tConsumerKey = "A2i1ee5iofqkf43f3Ts0X";
	private String m_tConsumerSecret = "Q4ux5qRH9GaH8tVwDCwInLy6z8snR";

	private static HashMap<String, String> m_cookie = new HashMap<String, String>();

	private String m_tToken;
	private String m_tTokenSecret;

	/**
	 * 创建一个新的实例 HttpClient.
	 */

	public HttpClient() {
		// TODO Auto-generated constructor stub
	}

	public HttpClient(String consumerKey, String consumerSecret) {
		this.m_tConsumerKey = consumerKey;
		this.m_tConsumerSecret = consumerSecret;
	}

	private void getCookie(HttpURLConnection huc) {
		try {
			String key = "";
			if (huc != null) {
				for (int i = 1; (key = huc.getHeaderFieldKey(i)) != null; i++) {
					if (key.equalsIgnoreCase("Set-Cookie")) {
						String sessionId = huc.getHeaderField(key);
						sessionId = sessionId.substring(0, sessionId.indexOf(";"));
						m_cookie.put(huc.getURL().getHost(), sessionId);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void setCookie(HttpURLConnection huc, HashMap<String, String> cookie) {

		try {

			if (huc != null) {
				if (cookie.get(huc.getURL().getHost()) != null) {
					System.out.println(cookie);
					huc.setRequestProperty("Cookie", cookie.get(huc.getURL().getHost()));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void setCookie(String host, String cookie) {

		try {
			m_cookie.put(host, cookie);
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

	public String doHttpRequest(String method, String url, List<PostParameter> postParams) {
		HttpURLConnection con = null;

		InputStream is = null;
		OutputStream os = null;

		String response = "";

		try {
			con = (HttpURLConnection) new URL(url).openConnection();

			setCookie(con, m_cookie);
			// con.setFollowRedirects(true);

			if (METHOD_GET.equals(method)) {
				if (null == postParams) {
					postParams = new ArrayList<PostParameter>(5);
				}
				parseQueryParameters(url, postParams);
			}

			con.setRequestMethod(method);

			con.setConnectTimeout(30000);
			con.setReadTimeout(30000);

			con.setDoInput(true);

			con.addRequestProperty("MEIZU_UA", "MEIZU");
			con.addRequestProperty("Authorization", generateAuthorizationHeader(method, url, postParams));

			if (Main.header != null && !Main.header.equals("")) {
				if (!con.getURL().getPath().contains("/oauth/access_token")) {
					try {
						JSONObject json = new JSONObject();
						Map map = (Map) json.parse(Main.header);
						Iterator it = map.keySet().iterator();
						while (it.hasNext()) {
							Object key = it.next();
							con.setRequestProperty(key.toString(), map.get(key).toString());
						}
					} catch (Exception e) {
						System.out.println("header error,must be json format");
						e.printStackTrace();
					}

				}

			}

			con.setRequestProperty("Accept-Language", Locale.getDefault().getLanguage().toLowerCase() + "-" + Locale.getDefault().getCountry().toLowerCase());

			if (METHOD_POST.equals(method)) {
				con.setDoOutput(true);
				con.setUseCaches(false);

				con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");

				byte[] reqData = encodeParameters(postParams, "&", false).getBytes("UTF-8");

				con.setRequestProperty("Content-Length", String.valueOf(reqData.length));

				os = con.getOutputStream();

				os.write(reqData);
				os.flush();
			}

			con.connect();

			if (200 == con.getResponseCode()) {
				is = con.getInputStream();
				getCookie(con);

			} else {
				is = con.getErrorStream();
			}

			if (null != is) {
				byte[] rspData = new byte[1024];
				ByteArrayOutputStream rspCache = new ByteArrayOutputStream();

				int nRead = -1;

				while (true) {
					nRead = is.read(rspData);
					if (nRead == -1) {
						break;
					}
					rspCache.write(rspData, 0, nRead);
				}

				// response = URLDecoder.decode(new
				// String(rspCache.toByteArray()), "UTF-8");
				response = new String(rspCache.toByteArray(), "utf-8");
			}
		} catch (IOException ex) {
			ex.printStackTrace();
		} finally {
			try {
				if (null != os) {
					os.close();
				}

				if (null != is) {
					is.close();
				}

				if (null != con) {
					con.disconnect();
				}
			} catch (IOException ex) {
				ex.printStackTrace();
			}
		}

		return response;
	}

	private String generateAuthorizationHeader(String method, String url, List<PostParameter> postParams) {
		List<PostParameter> oauthHeaderParams = new ArrayList<PostParameter>(5);

		oauthHeaderParams.add(new PostParameter("oauth_consumer_key", null == m_tConsumerKey ? "" : m_tConsumerKey));

		oauthHeaderParams.add(new PostParameter("oauth_signature_method", "MD5"));

		long timestamp = System.currentTimeMillis() / 1000;
		oauthHeaderParams.add(new PostParameter("oauth_timestamp", timestamp));

		long once = timestamp + m_tRandom.nextInt();
		oauthHeaderParams.add(new PostParameter("oauth_nonce", once));

		oauthHeaderParams.add(new PostParameter("oauth_version", "1.0"));

		if (null != m_tToken) {
			oauthHeaderParams.add(new PostParameter("oauth_token", m_tToken));
		}

		// if (Main.header != null && !Main.header.equals("")) {
		// try {
		// JSONObject json = new JSONObject();
		// Map map = (Map) json.parse(Main.header);
		// Iterator it = map.keySet().iterator();
		// while (it.hasNext()) {
		// Object key = it.next();
		// oauthHeaderParams.add(new PostParameter(key.toString(),
		// map.get(key).toString()));
		// }
		// } catch (Exception e) {
		// System.out.println("header error,must be json format");
		// e.printStackTrace();
		// }
		//
		// }

		List<PostParameter> signatureBaseParams = new ArrayList<PostParameter>(oauthHeaderParams.size() + (null == postParams ? 0 : postParams.size()));
		signatureBaseParams.addAll(oauthHeaderParams);
		if (null != postParams) {
			signatureBaseParams.addAll(postParams);
		}

		StringBuffer base = new StringBuffer(method).append("&");
		base.append(percentEncode(normalizeUrl(url)));
		base.append("&");

		Collections.sort(signatureBaseParams);

		base.append(percentEncode(encodeParameters(signatureBaseParams, "&", false)));

		base.append('&');
		base.append(percentEncode(null == m_tConsumerSecret ? "" : m_tConsumerSecret));
		base.append('&');

		if (null != m_tTokenSecret) {
			base.append(percentEncode(m_tTokenSecret));
		}

		String signature = generateSignature(base.toString());

		if (Main.isDebug) {
			System.out.println("OAuth base string:" + base.toString());
			System.out.println("OAuth signature:" + signature);
		}

		if (METHOD_GET.equals(method)) {
			signatureBaseParams.add(new PostParameter("oauth_signature", signature));
			return "OAuth " + encodeParameters(signatureBaseParams, ",", true);
		} else {
			oauthHeaderParams.add(new PostParameter("oauth_signature", signature));
			return "OAuth " + encodeParameters(oauthHeaderParams, ",", true);
		}
	}

	public boolean login(int loginType, String username, String password, String phoneSn) {
		List<PostParameter> postParams = new ArrayList<PostParameter>(5);

		if (LOGIN_TYPE_SN == loginType) {
			postParams.add(new PostParameter("x_auth_mode", "sn_auth"));
			postParams.add(new PostParameter("x_auth_sn", null == phoneSn ? "" : phoneSn));
		} else if (LOGIN_TYPE_CLIENT == loginType) {
			postParams.add(new PostParameter("x_auth_mode", "client_auth"));
			postParams.add(new PostParameter("x_auth_username", null == username ? "" : username));
			postParams.add(new PostParameter("x_auth_password", null == password ? "" : password));
			postParams.add(new PostParameter("x_auth_sn", null == phoneSn ? "" : phoneSn));
		}

		// postParams.add(new PostParameter("x_auth_rememberme","true"));
		// postParams.add(new PostParameter("flymeLogin", "true"));

		String rsp = doHttpRequest(METHOD_POST, GET_ACCESS_TOKEN_URL, postParams);

		if (!rsp.contains("oauth_token=")) {
			System.out.println(rsp);
			return false;
		}

		String[] rspStr = rsp.split("&");
		m_tToken = rspStr[0].substring(12);

		if (rsp.contains("oauth_token_secret=")) {
			m_tTokenSecret = rspStr[1].substring(19);
		}

		return true;
	}

	private void parseQueryParameters(String url, List<PostParameter> signatureParams) {
		int queryStart = url.indexOf("?");
		if (-1 != queryStart) {
			String[] queryStrs = url.substring(queryStart + 1).split("&");
			for (String query : queryStrs) {
				String[] split = query.split("=");
				if (split.length == 2) {
					signatureParams.add(new PostParameter(split[0], split[1]));
				} else {
					signatureParams.add(new PostParameter(split[0], ""));
				}
			}
		}
	}

	private String normalizeUrl(String url) {
		URI uri;
		try {
			uri = new URI(url);
		} catch (URISyntaxException ex) {
			ex.printStackTrace();
			return url;
		}

		String scheme = uri.getScheme().toLowerCase();
		String authority = uri.getAuthority().toLowerCase();
		boolean dropPort = (scheme.equals("http") && uri.getPort() == 80) || (scheme.equals("https") && uri.getPort() == 443);
		if (dropPort) {
			int index = authority.lastIndexOf(":");
			if (index >= 0) {
				authority = authority.substring(0, index);
			}
		}
		String path = uri.getRawPath();
		if (path == null || path.length() <= 0) {
			path = "/";
		}

		return scheme + "://" + authority + path;
	}

	private String percentEncode(String value) {
		if (value == null) {
			return "";
		}

		try {
			return URLEncoder.encode(value, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
		} catch (UnsupportedEncodingException ex) {
			ex.printStackTrace();
		}

		return value;
	}

	private String encodeParameters(List<PostParameter> postParams, String splitter, boolean quot) {
		StringBuffer buf = new StringBuffer();

		if (null != postParams) {
			boolean first = true;

			for (PostParameter param : postParams) {
				if (first) {
					first = false;
				} else {
					if (quot) {
						buf.append("\"");
					}
					buf.append(splitter);
				}

				buf.append(percentEncode(param.name));
				buf.append("=");

				if (quot) {
					buf.append("\"");
				}

				buf.append(percentEncode(param.value));
			}

			if (!first) {
				if (quot) {
					buf.append("\"");
				}
			}
		}

		return buf.toString();
	}

	private String generateSignature(String basestring) {
		byte[] byteHMAC;
		try {
			byteHMAC = MD5Util.MD5Encode(basestring.getBytes("UTF-8"));
		} catch (UnsupportedEncodingException ex) {
			byteHMAC = MD5Util.MD5Encode(basestring.getBytes());
		}

		return m_tBase64Encoder.encode(byteHMAC);
	}

	public String getM_tConsumerKey() {
		return m_tConsumerKey;
	}

	public void setM_tConsumerKey(String m_tConsumerKey) {
		this.m_tConsumerKey = m_tConsumerKey;
	}

	public String getM_tConsumerSecret() {
		return m_tConsumerSecret;
	}

	public void setM_tConsumerSecret(String m_tConsumerSecret) {
		this.m_tConsumerSecret = m_tConsumerSecret;
	}

	public String getM_tToken() {
		return m_tToken;
	}
}
